{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/Maplemx/Agently/blob/main/playground/human_step_in_before_reply.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Human Step In Before Reply"
      ],
      "metadata": {
        "id": "dAzfqHDCAXZe"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Demo Description\n",
        "\n",
        "**Author:** Agently Team\n",
        "\n",
        "**Prompt Language:** English\n",
        "\n",
        "**Agent Components:** EventListener, ReplyReformer\n",
        "\n",
        "**Description:**\n",
        "\n",
        "Sometimes we don't want the AI agent's response to be the final one directly to user. Instead, we want to have the opportunity to review the response and confirm it before it is sent to user. Or even, sometimes we hope to modify the reply.\n",
        "\n",
        "The following case demonstrates how to do that using Agently framework easily. We can pause the agent's output, undergo human review and verification, modify the reply if it is necessary. Then we can append the new reply into the chat history list.\n",
        "\n",
        "Through this case, we also can observe how AI agent dynamically adjusts its behavior based on the modified reply. (Maybe sometimes it is still stubborn, try add role settings using `.set_role()`)\n",
        "\n",
        "This is just a simple example. You can extend upon this by, for examples, using rule system instead of human verification, using this method to build your chatting samples in real time interaction with language model for model fine-tuning or just storing the samples into RoleManager (Read [Agently Application Development Handbook: RoleManager](https://github.com/Maplemx/Agently/blob/main/docs/guidebook/application_development_handbook.ipynb) to explore more).\n",
        "\n",
        "在一些场景下，我们并不希望AI Agent的回复就是最终回复，而是希望我们有机会在AI Agent给出回复前进行审查确认，甚至是介入修改回复。\n",
        "\n",
        "下面的案例给出了如何使用Agently完成这个过程。案例展现了如何暂停Agent输出，人工审核确认或是修订后再进行返回，并将返回结果写入对话记录的全过程。\n",
        "\n",
        "通过这个案例，我们也看到AI Agent在过程中根据修订结果，即时调整了自己的行为反馈。\n",
        "\n",
        "这只是一个简单的案例，你可以基于这个案例做进一步的扩展，例如：使用其他规则系统替代人工审核确认，使用这种交互方式训练你自己的对话样本集用于模型微调或是存入角色库等。\n",
        "\n",
        "关于角色库：想要了解更多，可以阅读[📖 Agently应用开发手册：RoleManager章节](https://github.com/Maplemx/Agently/blob/main/docs/guidebook/application_development_handbook.ipynb)"
      ],
      "metadata": {
        "id": "kyLFmv_l-aIx"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Step 1: Install Packages"
      ],
      "metadata": {
        "id": "nRsfMu4lAJZF"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install Agently"
      ],
      "metadata": {
        "id": "nsst7pOAANlr"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "## Step 2: Demo Code"
      ],
      "metadata": {
        "id": "_-1gryYwASPM"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "id": "lLaK-w-E-ZKU",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "db7f642f-fe0a-4973-9f79-8f8d560ee1ee"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "[USER]: Yo man\n",
            "[Assistant] want to response:\n",
            "What's up?\n",
            "Do you confirm? [Y/N]: n\n",
            "Correct reply: 😷\n",
            "[Assistant]:  😷\n",
            "[USER]: What happened?\n",
            "[Assistant] want to response:\n",
            "Sorry, I'm an AI language model and I don't have emotions or physical experiences. Is there something specific you'd like to talk about or ask me?\n",
            "Do you confirm? [Y/N]: n\n",
            "Correct reply: I can't sleep last night. I'm feeling sick right now.\n",
            "[Assistant]:  I can't sleep last night. I'm feeling sick right now.\n",
            "[USER]: Do you need me to drive you to the hospital?\n",
            "[Assistant] want to response:\n",
            "Thank you for your concern, but as an AI, I don't have a physical body. I appreciate your kindness though.\n",
            "Do you confirm? [Y/N]: n\n",
            "Correct reply: Yeah, that'll be good. It's so kind of you.\n",
            "[Assistant]:  Yeah, that'll be good. It's so kind of you.\n",
            "[USER]: No problem. Do you need anything else?\n",
            "[Assistant] want to response:\n",
            "Thank you for your offer, but I think getting to the hospital is the most important thing right now.\n",
            "Do you confirm? [Y/N]: y\n",
            "[Assistant]:  Thank you for your offer, but I think getting to the hospital is the most important thing right now.\n",
            "[USER]: Yeah, right. I'll be your place ASAP.\n",
            "[Assistant] want to response:\n",
            "I appreciate your willingness to help, but keep in mind that as an AI, I don't have a physical presence or need to visit a hospital. However, I'm here to chat and assist with any questions or concerns you may have.\n",
            "Do you confirm? [Y/N]: n\n",
            "Correct reply: Thanks\n",
            "[Assistant]:  Thanks\n",
            "[USER]: #exit\n",
            "------------\n",
            " [Chat History]: \n",
            " [{'role': 'user', 'content': 'Yo man'}, {'role': 'assistant', 'content': '😷'}, {'role': 'user', 'content': 'What happened?'}, {'role': 'assistant', 'content': \"I can't sleep last night. I'm feeling sick right now.\"}, {'role': 'user', 'content': 'Do you need me to drive you to the hospital?'}, {'role': 'assistant', 'content': \"Yeah, that'll be good. It's so kind of you.\"}, {'role': 'user', 'content': 'No problem. Do you need anything else?'}, {'role': 'assistant', 'content': 'Thank you for your offer, but I think getting to the hospital is the most important thing right now.'}, {'role': 'user', 'content': \"Yeah, right. I'll be your place ASAP.\"}, {'role': 'assistant', 'content': 'Thanks'}]\n"
          ]
        }
      ],
      "source": [
        "import Agently\n",
        "\n",
        "agent_factory = Agently.AgentFactory()\n",
        "agent_factory\\\n",
        "    .set_settings(\"model.OpenAI.auth\", { \"api_key\": \"\" })\n",
        "\n",
        "\n",
        "agent = agent_factory.create_agent()\n",
        "# We need to manage chat history manually in this case because\n",
        "# we want to change reply.\n",
        "# Agently Team To-Do:\n",
        "# - We will update Session component to support this use case\n",
        "#   to record chat history with edited reply automatically\n",
        "chat_history = []\n",
        "while True:\n",
        "    # Confirm reply or edit it\n",
        "    def human_confirm(data):\n",
        "        global edited_reply\n",
        "        confirm = \"\"\n",
        "        while confirm not in (\"y\", \"n\"):\n",
        "            confirm = input(\"\\nDo you confirm? [Y/N]: \").lower()\n",
        "        if confirm == \"n\":\n",
        "            while edited_reply == \"\":\n",
        "                edited_reply = input(\"Correct reply: \")\n",
        "    # Use ReplyReformer component to change reply\n",
        "    def change_reply(data):\n",
        "        if edited_reply == \"\":\n",
        "            return data[\"reply\"]\n",
        "        else:\n",
        "            return edited_reply\n",
        "\n",
        "    # Start main process\n",
        "    edited_reply = \"\"\n",
        "    global chat_history\n",
        "    user_input = input(\"[USER]: \")\n",
        "    if user_input == \"#exit\":\n",
        "        break\n",
        "    print(\"[Assistant] want to response:\")\n",
        "    # - on_delta: print response in realtime\n",
        "    # - on_done: pause output process and call `human_confirm()`\n",
        "    # - reform_reply: call `change_reply()` to edit final return\n",
        "    reply = agent\\\n",
        "        .chat_history(chat_history)\\\n",
        "        .input(user_input)\\\n",
        "        .on_delta(lambda data: print(data, end=\"\"))\\\n",
        "        .on_done(human_confirm)\\\n",
        "        .reform_reply(change_reply)\\\n",
        "        .start()\n",
        "    print(\"[Assistant]: \", reply)\n",
        "    chat_history.extend([\n",
        "        { \"role\": \"user\", \"content\": user_input },\n",
        "        { \"role\": \"assistant\", \"content\": reply }\n",
        "    ])\n",
        "print(\"------------\\n\", \"[Chat History]: \\n\", chat_history)"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "---\n",
        "\n",
        "[**_<font color = \"red\">Agent</font><font color = \"blue\">ly</font>_** Framework - Speed up your AI Agent Native application development](https://github.com/Maplemx/Agently)"
      ],
      "metadata": {
        "id": "IT3pSaO2NgkG"
      }
    }
  ]
}